package com.fy.market.strategy.service.assemble.template;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fy.market.strategy.model.entity.StrategyAwardRuleEntity;
import com.fy.market.strategy.model.entity.StrategyRuleEntity;
import com.fy.market.strategy.model.enums.RuleModelTypeEnum;
import com.fy.market.strategy.repository.*;
import com.fy.market.types.common.ex.ExHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 统一装配实现类
 * 包含所有装配逻辑的实现
 * 
 * @author fwq
 * @date 2025-09-19
 */
@Slf4j
@Service("unifiedAssemble")
public class UnifiedAssemble extends StrategyAwardAssembleTmp {

    @Resource
    private IStrategyRepository iStrategyRepository;

    public UnifiedAssemble(IStrategyRepository iStrategyRepository) {
        super(iStrategyRepository);
    }

    /**
     * 默认装配实现
     */
    @Override
    protected void doDefaultAssemble(Integer strategyId, List<StrategyAwardRuleEntity> strategyAwardRuleEntities, List<StrategyRuleEntity> strategyRuleEntities) {
        log.info("开始执行默认装配，策略ID: {}", strategyId);

        // 使用公共方法构建抽奖桶
        List<Integer> expandedAwardList = buildAwardBucket(strategyAwardRuleEntities);

        // 转为序号 -> 奖品ID 的映射（序号从1开始）
        Map<Integer, Integer> indexToAwardId = new ConcurrentHashMap<>();
        for (int i = 0; i < expandedAwardList.size(); i++) {
            indexToAwardId.put(i + 1, expandedAwardList.get(i));
        }

        // 存储到Redis
        iStrategyRepository.addDefaultRedisCache(strategyId, indexToAwardId);
        log.info("默认装配完成，策略ID: {}, 奖品总数: {}", strategyId, expandedAwardList.size());
    }

    /**
     * 权重装配实现
     */
    @Override
    protected void doRuleWeightAssemble(Integer strategyId, List<StrategyAwardRuleEntity> strategyAwardRuleEntities, List<StrategyRuleEntity> strategyRuleEntities) {
        log.info("开始执行权重装配，策略ID: {}", strategyId);

        // 1. 过滤出在抽奖前生效且模型为 rule_weight 的规则
        List<StrategyRuleEntity> strategyRuleEntityList = strategyRuleEntities.stream()
                .filter(strategyRuleEntity -> RuleModelTypeEnum.BEFORE_LOTTERY.getCode().equals(strategyRuleEntity.getRuleModelType())
                        && "rule_weight".equals(strategyRuleEntity.getRuleModel()))
                .collect(Collectors.toList());

        if(strategyRuleEntityList.isEmpty()) {
            log.info("当前策略没有权重规则，跳过权重装配");
            return;
        }

        // 2. 查询该策略的【次数 -> 奖品ID集合】分组
        Map<Integer, List<Integer>> ruleWeightMap = iStrategyRepository.queryRuleWeightGroupByTimes(strategyId);
        if (CollectionUtils.isEmpty(ruleWeightMap)) {
            log.info("当前策略没有权重数据，跳过权重装配");
            return;
        }

        // 3. 以次数为维度，构建每个范围的概率表
        Map<Integer, Map<Integer, Integer>> rangeIndexToAwardIdMap = new ConcurrentHashMap<>();
        List<Integer> sortedTimes = ruleWeightMap.keySet().stream().sorted().collect(Collectors.toList());
        Integer allRate = 0;

        for (Integer times : sortedTimes) {
            List<Integer> awardIdsAtTimes = ruleWeightMap.get(times);
            if (CollectionUtils.isEmpty(awardIdsAtTimes)) continue;

            // 在当前范围下生效的奖品实体子集
            List<StrategyAwardRuleEntity> activeAwards = strategyAwardRuleEntities.stream()
                    .filter(sa -> awardIdsAtTimes.contains(sa.getAwardId()))
                    .collect(Collectors.toList());

            if (CollectionUtils.isEmpty(activeAwards)) continue;

            // 健壮性校验
            activeAwards.forEach(entity -> {
                ExHandler.throwIf(entity.getAwardRate() == null, "奖品概率为空，数据异常！");
                ExHandler.throwIf(entity.getAwardId() == null, "奖品ID为空，数据异常！");
            });

            // 使用公共方法构建抽奖桶
            List<Integer> expandedAwardList = buildAwardBucket(activeAwards);

            // 转为序号 -> 奖品ID 的映射（序号从1开始）
            Map<Integer, Integer> indexToAwardId = new ConcurrentHashMap<>();
            for (int i = 0; i < expandedAwardList.size(); i++) {
                indexToAwardId.put(i + 1, expandedAwardList.get(i));
            }

            rangeIndexToAwardIdMap.put(times, indexToAwardId);
            allRate += expandedAwardList.size();
            iStrategyRepository.addRuleWeightRedisCache(strategyId, expandedAwardList.size(), times, rangeIndexToAwardIdMap);
        }

        iStrategyRepository.addAllRateCache(strategyId, allRate);
        log.info("权重装配完成，策略ID: {}, 总奖品数: {}", strategyId, allRate);
    }

    /**
     * 策略奖品库存装配钩子方法
     * @param strategyId
     */
    @Override
    protected void doStrategyAwardStockAssemble(Integer strategyId) {
        iStrategyRepository.addStockCache(strategyId);
    }


    // ==================== 公共方法 ====================

    /**
     * 构建抽奖桶 - 核心公共方法
     * 计算概率并生成打散后的奖品序列
     * 
     * @param strategyAwardRuleEntities 奖品规则实体列表
     * @return 打散后的奖品ID列表（抽奖桶）
     */
    private List<Integer> buildAwardBucket(List<StrategyAwardRuleEntity> strategyAwardRuleEntities) {
        // 计算总概率与最小概率
        BigDecimal totalAwardRate = strategyAwardRuleEntities.stream()
                .map(StrategyAwardRuleEntity::getAwardRate)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        ExHandler.throwIf(totalAwardRate == null || totalAwardRate.compareTo(BigDecimal.ZERO) == 0, "当前范围总概率为0，无法计算！");

        BigDecimal minAwardRate = strategyAwardRuleEntities.stream()
                .map(StrategyAwardRuleEntity::getAwardRate)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);

        // 修复：添加对最小概率为零的检查，防止除零异常
        ExHandler.throwIf(minAwardRate == null || minAwardRate.compareTo(BigDecimal.ZERO) <= 0, "最小奖品概率为0或负数，无法计算！");

        // 与已有实现保持一致的比例范围与次数计算
        BigDecimal rateRange = totalAwardRate.divide(minAwardRate, 0, RoundingMode.CEILING);

        // 构建当前范围的奖品序列（用于后续打散及映射）
        List<Integer> expandedAwardList = new ArrayList<>(rateRange.intValue());
        for (StrategyAwardRuleEntity strategyAwardRuleEntity : strategyAwardRuleEntities) {
            BigDecimal awardRate = strategyAwardRuleEntity.getAwardRate();
            long appearCount = totalAwardRate.divide(awardRate, 0, RoundingMode.CEILING).longValue();
            for (int i = 0; i < appearCount; i++) {
                expandedAwardList.add(strategyAwardRuleEntity.getAwardId());
            }
        }

        // 打散，增强随机性
        Collections.shuffle(expandedAwardList);
        
        return expandedAwardList;
    }


} 